#!/usr/bin/env node

const os= require('os');
const command = require('./js/command');
const path = require('path');
const fs = require('fs-extra');
const iconv = require("iconv-lite");
const _ = require('lodash');
const shell = require('shelljs');
const xjava = process.platform !== 'win32' ? 'xjava' : `"${shell.env.SHELL}" "${ path.join(os.homedir(), 'command/xjava').replace(/\\/g, '/')}"`;
const javaFile = path.join(os.homedir(), 'command/java/Test.java');
let headers = getDefaultHeaders();
let codes = [];
let _mode = false;

function getDefaultHeaders() {
    const text = shell.exec(`${xjava} -k ""`, { silent:true }).stdout;
    let list = text ? text.split('\n').filter(o=>o) : [];
    list = [
        ...list,
        "import com.remobile.moment.Moment;",
        "import static com.remobile.moment.Moment.moment;",
        "import com.github.underscore.Predicate;",
        "import com.github.underscore.lodash.U;",
        "import java.util.Arrays;",
        "import java.util.*;",
        // "import org.apache.commons.lang3.*;",
        // "import com.alibaba.fastjson.*;",
        "import java.util.regex.*;",
    ];
    return list;
}
function getFullCode(hasLineNumber) {
    let _headers, _codes;
    if (hasLineNumber) {
        const len = headers.length;
        _headers = headers.map((o, i) => `${i+1}: ${o}`);
        _codes = codes.map((o, i) => `${i+1+len}: ${o}`);
    } else {
        _headers = headers;
        _codes = codes;
    }
    return `${_headers.join('\n')}\npublic class Test {\n  public static void main(String[] args) {\n    ${_codes.join('\n    ')}\n  }\n}`;
}
function syncCodeFromFile() {
    if (fs.existsSync(javaFile)) {
        const text = fs.readFileSync(javaFile, 'utf8');
        const lines = _.dropRight(text.split('\n').filter(o=>o.trim()), 2);
        let code = false;
        headers = [];
        codes = [];
        for (const line of lines) {
            if (/public class Test|public static void main/.test(line)) {
                code = true;
            } else {
                code && codes.push(line.trim()) || headers.push(line.trim());
            }
        }
    }
    this.prompt();
}
function clearCodes(line) {
    if (!line) {
        codes = [];
    } else if (line === 'all') {
        text = getFullCode();
        const file = path.join(os.homedir(), '.xn_history/.xn_java_code_history');
        fs.appendFileSync(file, '\n\n==========================\n\n');
        fs.appendFileSync(file, text);
        codes = [];
        headers = getDefaultHeaders();
    } else {
        const list = line.split(/\s+/);
        let indexes = [];
        for (const item of list) {
            if (+item == item) {
                indexes.push(+item);
            } else {
                const t = item.split('-');
                indexes.splice(0, 0, ..._.range(t[0], t[1]*1+1));
            }
        }
        indexes = indexes.map(o => +o - 1).sort();
        const len = headers.length;
        const hIndexes = indexes.filter(o => o < len);
        const dIndexes = indexes.filter(o => o >= len).map(o => o - len);
        _.remove(headers, (o, k) => _.includes(hIndexes, k));
        _.remove(codes, (o, k) => _.includes(dIndexes, k));
    }
    showCode.call(this);
    this.prompt();
}
function showCode() {
    this.print(getFullCode(true), 'blue');
    this.prompt();
}
function showJar(line) {
    this.print(shell.exec(`${xjava} -d "${line}"`, { silent:true }).stdout, 'blue');
    this.prompt();
}
function showHeaders(line) {
    this.print(shell.exec(`${xjava} -k "${line}"`, { silent:true }).stdout, 'blue');
    this.prompt();
}
function format(line) {
    if (/log\(.*\)/.test(line)) {
        line = line.replace('log(', 'System.out.println(');
    }
    if (/(?<!Arrays\.)asList\(.*\)/.test(line)) {
        line = line.replace('asList(', 'Arrays.asList(');
    }
    return line;
}
function appendCode(line) {
    if (!line) {
        return this.prompt();
    }
    const lastWord = _.last(line);
    if (!_.includes(['}', ';'], lastWord)) {
        line = `${line};`;
    }
    let isHeader;
    if (/^import/.test(line)) {
        isHeader = true;
        headers.push(line);
    } else {
        codes.push(format(line));
    }
    if (_mode) {
        runCode.call(this, 0, isHeader);
    } else {
        this.prompt();
    }
}
function runCode(mode, isHeader) {
    let text;
    if (mode == 0 || mode == 1) {
        text = getFullCode();
        fs.writeFileSync(javaFile, text);
    } else {
        syncCodeFromFile.call(this);
        text = fs.readFileSync(javaFile);
    }
    shell.exec(`${xjava} -s`, { silent:true, encoding: 'buffer' }, (code, stdout, stderr) => {
        if (stderr.length) {
            if (mode == 0) {
                if (isHeader) {
                    headers = _.dropRight(headers);
                } else {
                    codes = _.dropRight(codes);
                }
            }
            //this.dropHistory();
            stderr = require("iconv-lite").decode(Buffer.from(stderr, 'binary'), 'gb2312');
            this.print(stderr, 'red');
        } else if (stdout.length) {
            stdout = require("iconv-lite").decode(Buffer.from(stdout, "binary"), 'gb2312');
            this.print(text, 'blue');
            this.print(stdout, 'green');
        } else {
            this.print(text, 'blue');
        }
        this.prompt();
    });
}
function openCodeWithCode() {
    this.verbose(javaFile);
    shell.exec(`code ${javaFile}`, { silent:true });
    this.prompt();
}
function openCodeWithIdea() {
    this.verbose(javaFile);
    shell.exec(`"/Applications/IntelliJ IDEA.app/Contents/MacOS/idea" ${javaFile}`, { silent:true });
    this.prompt();
}
function toggleMode() {
    _mode = !_mode;
    this.print('模式切换至 ' + (_mode ? '及时运行' : '代码运行'));
    this.prompt();
}
function showHelp () {
    this.print('commands:', 'blue');
    this.print('    <h|help>: show help');
    this.print('    <q|exit>: exit');
    this.print('    <j|jar>: 显示和查看classpath的jar包');
    this.print('    <d|header>: 显示和查看固定的header');
    this.print('    <c|clear>: 清除缓存的内容，如果后面有数字，则清除对应的内容, c 1-6 7 8-9|all');
    this.print('    <s|show>: 显示缓存的内容');
    this.print('    <sh|showHistory> [n = 10]: 显示n个历史');
    this.print('    <uh|useHistory> [index = 1]: 按照序号使用历史');
    this.print('    <sync|syncCodeFromFile>: 同步文件的代码到内存');
    this.print('    <a|code>: 使用code打开Test.java文件');
    this.print('    <i|idea>: 使用idea打开Test.java文件');
    this.print('    <r|run> [true]: 运行存在的代码，true则运行内存代码');
    this.print('    _: 切换及时运行(写一行代码就运行)和代码运行');
    this.print('');
    this.prompt();
}
const COMMANDS = {
    'h|help': showHelp,
    'q|exit': function () { this.close() },
    'cls': function() { this.clear() },
    'sh|showHistory': function (n) { this.showHistory(n) },
    'uh|useHistory': function (n) { this.useHistory(n) },
    'sync|syncCodeFromFile': syncCodeFromFile,
    'j|jar': showJar,
    'd|header': showHeaders,
    'c|clear': clearCodes,
    's|show': showCode,
    'a|code': openCodeWithCode,
    'i|idea': openCodeWithIdea,
    'r|run': function(n) { runCode.call(this, n ? 1 : 2) },
    '_': toggleMode,
    'default': appendCode,
};

command(COMMANDS, { title: 'java', callback: syncCodeFromFile });
